﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace YouWei.Api.Common
{
    /// <summary>
    /// 敏感词过滤
    /// </summary>
    public class IllegalWordsSearch : BaseSearchEx
    {
        /// <summary>
        /// 使用忽略大小写
        /// </summary>
        public bool UseIgnoreCase = true;


        private static readonly IllegalWordsSearch instance = null;
        static IllegalWordsSearch()
        {
            instance = new IllegalWordsSearch();
        }

        private IllegalWordsSearch()
        {
        }

        /// <summary>
        /// 实例化
        /// </summary>
        public static IllegalWordsSearch Instance
        {
            get
            {
                return instance;
            }
        }

        /// <summary>
        /// 在文本中替换所有的关键字
        /// </summary>
        /// <param name="text">文本</param>
        /// <param name="replaceChar">替换符</param>
        /// <returns></returns>
        public string Replace(string text, char replaceChar)
        {
            StringBuilder result = new StringBuilder(text);

            int[] pIndex = new int[text.Length];
            var p = 0;
            int findIndex = 0;
            char pChar = (char)0;


            for (int i = 0; i < text.Length; i++)
            {
                if (p != 0)
                {
                    pIndex[i] = p;
                    if (findIndex != 0)
                    {
                        foreach (var item in _guides[findIndex])
                        {
                            var r = GetIllegalResult(item, i - 1, text, p, pIndex);
                            if (r != null)
                            {
                                for (int j = r.Start; j < i; j++) { result[j] = replaceChar; }
                                break;
                            }
                        }
                    }
                }

                var c = text[i];
                var t = _dict[ToSenseWord(c)];
                if (t == 0) { p = 0; pChar = c; continue; }//不在字表中，跳过

                var next = _next[p] + t;
                bool find = _key[next] == t;
                if (find == false)
                {
                    if (p != 0)
                    {
                        p = 0;
                        next = _next[0] + t;
                        find = _key[next] == t;
                    }
                }
                if (find)
                {
                    findIndex = _check[next];
                    p = next;
                }
                pChar = c;
            }
            if (findIndex != 0)
            {
                foreach (var item in _guides[findIndex])
                {
                    var r = GetIllegalResult(item, text.Length - 1, text, p, pIndex);
                    if (r != null)
                    {
                        for (int j = r.Start; j < text.Length; j++) { result[j] = replaceChar; }
                        break;
                    }
                }
            }
            return result.ToString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="keyword"></param>
        /// <param name="end"></param>
        /// <param name="srcText"></param>
        /// <param name="p"></param>
        /// <param name="pIndex"></param>
        /// <returns></returns>
        private int FindStart(string keyword, int end, string srcText, int p, int[] pIndex)
        {
            if (end + 1 < srcText.Length)
            {
                var en1 = IsEnglishOrNumber(srcText[end + 1]);
                var en2 = IsEnglishOrNumber(srcText[end]);
                if (en1 && en2) { return -1; }
            }
            var n = keyword.Length;
            var start = end;
            int pp = p;
            while (n > 0)
            {
                var pi = pIndex[start--];
                if (pi != pp) { n--; pp = pi; }
                if (start == -1) return 0;
            }
            var sn1 = IsEnglishOrNumber(srcText[start++]);
            var sn2 = IsEnglishOrNumber(srcText[start]);
            if (sn1 && sn2)
            {
                return -1;
            }
            return start;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="index"></param>
        /// <param name="end"></param>
        /// <param name="srcText"></param>
        /// <param name="p"></param>
        /// <param name="pIndex"></param>
        /// <param name="flag"></param>
        /// <returns></returns>
        private IllegalWordsSearchResult GetIllegalResult(int index, int end, string srcText, int p, int[] pIndex)
        {
            var keyword = _keywords[index];
            if (keyword.Length == 1)
            {
                if (ToSenseWord(srcText[end]) != ToSenseWord(keyword[0])) { return null; }
                return new IllegalWordsSearchResult(keyword, end, end, srcText);
            }
            var start = FindStart(keyword, end, srcText, p, pIndex);
            if (start == -1) { return null; }
            if (ToSenseWord(srcText[start]) != ToSenseWord(keyword[0])) { return null; }

            return new IllegalWordsSearchResult(keyword, start, end, srcText);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private char ToSenseWord(char c)
        {
            if (UseIgnoreCase)
            {
                if (c >= 'A' && c <= 'Z') return (char)(c | 0x20);
            }
            return c;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private string ToSenseWord(string text)
        {
            StringBuilder stringBuilder = new StringBuilder(text.Length);
            for (int i = 0; i < text.Length; i++)
            {
                stringBuilder.Append(ToSenseWord(text[i]));
            }
            return stringBuilder.ToString();
        }

        /// <summary>
        /// 设置关键字
        /// </summary>
        /// <param name="keywords">关键字列表</param>
        public override void SetKeywords(ICollection<string> keywords)
        {
            HashSet<string> kws = new HashSet<string>();
            foreach (var item in keywords)
            {
                kws.Add(ToSenseWord(item));
            }
            base.SetKeywords(kws);
        }

        private bool IsEnglishOrNumber(char c)
        {
            if (c < 128)
            {
                if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
                {
                    return true;
                }
            }
            return false;
        }

    }
}